package com.tensquare.article.service;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.tensquare.article.client.NoticeClient;
import com.tensquare.article.client.entity.Notice;
import com.tensquare.article.config.RabbitmqConfig;
import com.tensquare.article.dao.ArticleDao;
import com.tensquare.article.pojo.Article;
import com.tensquare.entity.PageResult;
import com.tensquare.exception.TensquareException;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import util.IdWorker;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
public class ArticleService {

    @Autowired(required = false)
    private ArticleDao articleDao;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private NoticeClient noticeClient;

    @Autowired
    private DirectExchange directExchange;

    @Autowired
    private RabbitAdmin rabbitAdmin;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 添加文章
     * @param article
     */
    public void add(Article article) {
        // 生成唯一ID
        article.setId(idWorker.nextId() + "");
        article.setCreatetime(new Date());
        article.setIspublic("0");// 不公开
        article.setIstop("0");// 不置顶
        article.setVisits(0);
        article.setThumbup(0);
        article.setComment(0);
        article.setState("0");//未审核

        // 插入文章表
        articleDao.insert(article);

        // 调用通知微服，通知所有订阅了这个作者的用户
        // 取出订阅了这个作者的用户集合
        String author_users = "artilce_author_" + article.getUserid();
        // members 订阅了这个作者的用户集合
        Set<String> members = redisTemplate.opsForSet().members(author_users);
        // 循环调用,
        if(null != members){
            Notice notice = new Notice();
            notice.setAction("publish");// 发布文章
            notice.setType("sys"); // 系统通知
            notice.setOperatorId(article.getUserid()); // 文章的作者
            notice.setTargetType("article"); // 文章 业务
            notice.setTargetId(article.getId());// 文章 的编号
            for (String userId : members) {
                notice.setReceiverId(userId);// 接收者就订阅了这个作者的用户
                // 调用通知微服
                noticeClient.add(notice);
            }
        }

        // 发送消息给MQ，指定交换与routingKey=作者的id
        rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ARTICLE, article.getUserid(), article.getId());

        // 优化方案：只要调用一个通知微服的群发功能传递作者的id
        //    通过作者id获取redis中所有的用户，添加通知即可，只需要调用一次通知微服即可
    }

    /**
     * 通过Id查询文章信息
     * @param articleId
     * @return
     */
    public Article findById(String articleId) {
        /*if(1==1){
                throw new TensquareException("测试自定义异常");
        }*/
        return articleDao.selectById(articleId);
    }

    /**
     * 查询所有文章列表
     * @return
     */
    public List<Article> findAll() {
        return articleDao.selectList(null);
    }

    /**
     * 更新文章
     * @param article
     */
    public void update(Article article) {
        // 最后更新时间
        article.setUpdatetime(new Date());
        articleDao.updateById(article);
    }

    /**
     * 通过id删除
     * @param articleId
     */
    public void deleteById(String articleId) {
        articleDao.deleteById(articleId);
    }

    /**
     * 分页条件查询
     * @param page
     * @param size
     * @param article
     * @return
     */
    public PageResult<Article> findPage(int page, int size, Article article) {
        // 分页信息构建  PageHelper.startPage
        Page<Article> articlePage = new Page<>(page,size);
        // 条件构建
        EntityWrapper wrapper = new EntityWrapper();
        // 条件判断
        if(!StringUtils.isEmpty(article.getUserid())){
            // 作者id不为空
            // where userid=值
            wrapper.eq("userid", article.getUserid());
        }
        if(!StringUtils.isEmpty(article.getTitle())){
            // 标题不为空，实现模糊查询
            // where tile like %s%
            // SqlLike.DEFAULT 左右两边自动补上%
            wrapper.like("title", article.getTitle());
        }
        // 调用dao查询
        List<Article> list = articleDao.selectPage(articlePage, wrapper);
        // 返回分页的结果
        return new PageResult<>(articlePage.getTotal(), list);
    }

    /**
     * 订阅/或取消
     * @param artilceId
     * @param userId
     * @return
     */
    public boolean substribe(String artilceId, String userId) {
        // 获取作者的id
        Article article = articleDao.selectById(artilceId);
        if(null == article){
            throw new TensquareException("没有找到对应的文章");
        }
        // 判断用户是否订阅过这个作者了
        String user_authors = "article_subscribe_" + userId;
        SetOperations userAuthorsSet = redisTemplate.opsForSet();
        // 作者的id
        String authorId = article.getUserid();
        String author_users = "artilce_author_" + authorId;


        // 创建MQ队列, 队列名称
        String queueName = user_authors;
        Queue queue = new Queue(queueName);
        // 绑定交换机与队列
        Binding binding = BindingBuilder.bind(queue).to(directExchange).with(authorId);

        if(!userAuthorsSet.isMember(user_authors, authorId)) {
        //========================订阅====================================
            // 如果没有订阅过，则要使用redis set集合保存 用户订阅的作者集合
            userAuthorsSet.add(user_authors, authorId);
            // 同时保存作者的订阅用户集合，有哪些用户订阅了这个作者
            redisTemplate.opsForSet().add(author_users, userId);

            //在MQ服务器上创建MQ队列
            rabbitAdmin.declareQueue(queue);
            // 在MQ服务器上绑定
            rabbitAdmin.declareBinding(binding);

            return true;
        }

        //=================取消订阅============================
        // 如果 是订阅过了，则要从 用户的订阅作者集合中移除作者id
        userAuthorsSet.remove(user_authors, authorId);
        //                  则要从 作者的用户集合中移除用户的id
        redisTemplate.opsForSet().remove(author_users, userId);

        // MQ的操作
        // 删除用户队列与交换机的绑定关系
        rabbitAdmin.removeBinding(binding);

        return false;
    }

    /**
     * 文章点赞 防止重复点赞
     * @param articleId
     * @param userId
     */
    public void thumbup(String articleId, String userId) {
        // 判断是否点赞过, 建议的方案使用redis的bitmap类型
        String key = "article_thumbup_" + articleId + "_" + userId;
        if(null != redisTemplate.opsForValue().get(key)){
            // 点赞过了
            throw new TensquareException("不能重复点赞");
        }

        // 可以点赞
        Article article = articleDao.selectById(articleId);
        article.setThumbup(article.getThumbup() + 1);
        articleDao.updateById(article);
        // 记录用户点赞了这篇文章，防止重复点赞
        redisTemplate.opsForValue().set(key,articleId);
        // 通知作者
        String authorId = article.getUserid();
        Notice notice = new Notice();
        notice.setAction("thumbup");// 发布文章
        notice.setType("user"); // 点对点，用户对用户的操作
        notice.setOperatorId(userId); // 登陆用户
        notice.setTargetType("article"); // 文章 业务
        notice.setTargetId(article.getId());// 文章 的编号
        notice.setReceiverId(authorId); // 接收者为作者
        // 调用通知 微服
        noticeClient.add(notice);
    }
}
